/*
 * UNIVERSIDADE FEDERAL DE CAMPINA GRANDE
 * CENTRO DE ENGENHARIA ELETRICA E COMPUTACAO
 * DEPARTAMENTO DE SISTEMAS E COMPUTACAO
 * 
 * EQUIPE:
 * Francisco Gomes de Oliveira Neto
 * Magno Jefferson de Souza Queiroz
 * 
 * Projeto de Software Orientado a Objeto
 */
package util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


import datamanager.Conta;
import datamanager.Entrada;
import datamanager.Gasto;
import exceptions.MyMoneyException;

public class RelatorioPrinter {
	
	private static final int[] COMPRIMENTOS_VALORES = {8,13,10,8,25};
	private static final int OPER_IND  = 0;
	private static final int IDENT_IND = 1;
	private static final int DATA_IND  = 2;
	private static final int VIA_IND   = 3;
	private static final int DESC_IND  = 4;
	private static final int VALOR_IND = 5;
	
	private static final String EOL = System.getProperty("line.separator");
	private static final String SEP_COLUNAS = " |";
			
	private List<Entrada> entradas;
	private List<Gasto> gastos;
	private List<Conta> contas;
	private DecimalFormat df = new DecimalFormat("#0.00");
	
	private Map<String, Double> relatorioDeGastos;
	
	private double totalGastos;
	private double totalEntradas;
	private double totalContas;
	
	public RelatorioPrinter(List<Entrada> entradas, List<Gasto> gastos,
			List<Conta> contas) {
		this.entradas = entradas;
		this.gastos = gastos;
		this.contas = contas;
		this.totalGastos = 0.0;
		this.totalEntradas = 0.0;
		this.totalContas = 0.0;
		relatorioDeGastos = new HashMap<String, Double>();
	}

	public RelatorioPrinter(){
		getRelatorioSimples();
	}
	
	private String formatCabecalho(){
		String[] valoresCabecalho = new String[6];
		valoresCabecalho[OPER_IND] = "OPERACAO";
		valoresCabecalho[IDENT_IND]= "IDENTIFICADOR";
		valoresCabecalho[DATA_IND] = "DATA";
		valoresCabecalho[VIA_IND]  = "VIA PGTO";
		valoresCabecalho[DESC_IND] = "DESCRICAO";
		valoresCabecalho[VALOR_IND]= "VALOR";
		return formatLinha(valoresCabecalho);
	}
	
	private String getSeparadorCabecalho(){
		String separador = "=";
		for (int i = 0; i < 90; i++) {
			separador += "=";
		}
		return separador+EOL;	
	}
	
	public String getRelatorioSimples(){
		StringBuffer relatorio = new StringBuffer();
		relatorio.append(getSeparadorCabecalho());
		relatorio.append(formatCabecalho());
		relatorio.append(getSeparadorCabecalho());
		
		relatorio.append(formatGasto());
		relatorio.append(formatConta());
		relatorio.append(formatEntrada());
		System.out.println("++++++++++++++++++");
		
		System.out.println(df.format(totalGastos));
		System.out.println(df.format(totalEntradas));
		System.out.println(df.format(totalContas));
		for(String s : relatorioDeGastos.keySet()){
			double valor = relatorioDeGastos.get(s).doubleValue();
			System.out.println(s+" --> "+df.format(valor));
		}
		return relatorio.toString();		 
	}
	
	private String formatResumo(){
		StringBuffer sb = new StringBuffer();
		sb.append(getSeparadorCabecalho());
		sb.append("DESPESAS E RESPECTIVAS CATEGORIAS"+EOL);
		sb.append(getSeparadorCabecalho());
		for(String s : relatorioDeGastos.keySet()){
			double valor = relatorioDeGastos.get(s).doubleValue();
			sb.append(formatEsquerda(s, 30));
			sb.append(df.format(valor));
			sb.append(EOL);
		}
		sb.append(getSeparadorCabecalho());
		sb.append("RESUMO DAS OPERACOES"+EOL);
		sb.append(getSeparadorCabecalho());
		sb.append(geraLinhaResumo("TOTAL GASTOS: ", totalGastos));
		sb.append(geraLinhaResumo("TOTAL CONTAS: ", totalContas));
		sb.append(geraLinhaResumo("TOTAL ENTRADAS: ", totalEntradas));
		sb.append(getSeparadorCabecalho());
		sb.append(geraLinhaResumo("TOTAL: ", totalEntradas-(totalContas+totalGastos)));
		sb.append(getSeparadorCabecalho());
		
		return sb.toString();
	}
	
	private String geraLinhaResumo(String label, double valor){
		StringBuffer linhaResumo = new StringBuffer();
		linhaResumo.append(formatEsquerda(label, 16));
		linhaResumo.append(df.format(valor));
		linhaResumo.append(EOL);
		return linhaResumo.toString();		
	}
	
	public String geraRelatorioCompleto(){
		StringBuffer relatorioCompleto = new StringBuffer();
		relatorioCompleto.append(getRelatorioSimples());
		relatorioCompleto.append(formatResumo());

		//continuar
		return relatorioCompleto.toString();		
	}
	
	
	private String formatGasto(){
		StringBuffer linha = new StringBuffer();
		// Linha de gasto.
		for (int i = 0; i < gastos.size(); i++) {
			Gasto gasto = gastos.get(i);
			String[] atributosDoGasto  = new String[6];

			atributosDoGasto[OPER_IND] = gasto.toString();
			atributosDoGasto[IDENT_IND]= gasto.getId();
			atributosDoGasto[DATA_IND] = gasto.getData().toString();			
			atributosDoGasto[VIA_IND]  = gasto.getFormaPagamento();			

			
			
			String descricaoDoGasto = gasto.getCategoria();
			atributosDoGasto[DESC_IND] = descricaoDoGasto;
			
			double valorDoGasto = gasto.getValor();
			totalGastos = totalGastos + valorDoGasto;
			DecimalFormat df = new DecimalFormat("#0.00");
			atributosDoGasto[VALOR_IND] = "-"+df.format(valorDoGasto);
			linha.append(formatLinha(atributosDoGasto));
			
			if(relatorioDeGastos.containsKey(descricaoDoGasto)){
				double valorAtual = relatorioDeGastos.get(descricaoDoGasto).doubleValue();
				valorDoGasto = valorDoGasto + valorAtual;
			}
			relatorioDeGastos.put(descricaoDoGasto, valorDoGasto);
		}
		return linha.toString();
	}
	
	private String formatLinha(String[] valoresColunas){
		final String EOL = System.getProperty("line.separator");
		StringBuffer sb = new StringBuffer();
		// A ultima coluna (valor do gasto/entrada/conta) nao possui comprimento
		// definido.
		for (int i = 0; i < valoresColunas.length - 1; i++) {
			sb.append(formatEsquerda(valoresColunas[i], COMPRIMENTOS_VALORES[i]));
			sb.append(formatEsquerda(SEP_COLUNAS, 3));
		}
		sb.append(valoresColunas[valoresColunas.length-1]);
		sb.append(EOL);
		return sb.toString();
	}
	
	private String formatConta(){		
		StringBuffer linha = new StringBuffer();
		// Linha de contas a pagar.
		for (int i = 0; i < contas.size(); i++) {
			Conta conta = contas.get(i);
			
			String[] atributosDaConta = new String[6];
			atributosDaConta[OPER_IND] = conta.toString();
			atributosDaConta[IDENT_IND]= conta.getId();
				
			atributosDaConta[DATA_IND] = conta.getData().toString();
			atributosDaConta[VIA_IND]  = " ";
			String descricaoDaConta = conta.getCategoria();
			atributosDaConta[DESC_IND] = descricaoDaConta;
			
			DecimalFormat df = new DecimalFormat("#0.00");
			double valorConta = conta.getValor();
			totalContas = totalContas + valorConta;
			atributosDaConta[VALOR_IND] = "-"+df.format(valorConta);						
			linha.append(formatLinha(atributosDaConta));
			
			if(relatorioDeGastos.containsKey(descricaoDaConta)){
				double valorAtual = relatorioDeGastos.get(descricaoDaConta).doubleValue();
				valorConta  = valorConta + valorAtual;
			}
			relatorioDeGastos.put(descricaoDaConta, valorConta);
			
		}
		return linha.toString();
	}

	private String formatEntrada(){		
		StringBuffer linha = new StringBuffer();
		// Linha de entradas.
		for (int i = 0; i < entradas.size(); i++) {
			Entrada entrada = entradas.get(i);
			
			String[] atributosDaEntrada = new String[6];
			atributosDaEntrada[OPER_IND] = entrada.toString();
			atributosDaEntrada[IDENT_IND]= entrada.getId();
			atributosDaEntrada[DATA_IND] = entrada.getData().toString();
			atributosDaEntrada[VIA_IND]  = " ";
			atributosDaEntrada[DESC_IND] = " ";
			
			DecimalFormat df = new DecimalFormat("#0.00");
			double valorEntrada = entrada.getValor();
			totalEntradas = totalEntradas + valorEntrada;

			atributosDaEntrada[VALOR_IND] = " "+df.format(valorEntrada);		
			linha.append(formatLinha(atributosDaEntrada));
		}
		return linha.toString();
	}


	/**
	 * Format to the left a specified String to fill/fit in a specified length.
	 * 
	 * @param label
	 *            The String to be formatted.
	 * @param totalLength
	 *            The total length to adjust the string.
	 * @return The string formatted in the specified length.
	 */
	private String formatEsquerda(String label, int totalLength)
	{
		StringBuffer stringBuffer = new StringBuffer();	
		int numOfBlanks = totalLength - label.length();
		
		if(numOfBlanks >= 0){
			stringBuffer.append(label);
			stringBuffer.append(addBranco(numOfBlanks));			
		}else{
			stringBuffer.append(quebraString(label, (-1)*numOfBlanks));
		}
		return stringBuffer.toString();
		
	}
	/**
	 * Return a specified number of blanks (" ") to be added to a String.
	 * 
	 * @param numOfBlanks
	 *            The number of blanks to be added to the String.
	 * @return A string with the specified number of blanks (" ").
	 */
	private static String addBranco(int numOfBlanks)
	{
		StringBuffer stringBuffer = new StringBuffer();
		for(int i = 0; i < numOfBlanks; i++){
			stringBuffer.append(" ");
		}
		return stringBuffer.toString();
	}
	/**
	 * Split a string to fit in the specified length. If the String's length is
	 * smaller than the specified length, the string returned will be the
	 * specified string.
	 * 
	 * @param label
	 *            The String that needs to be adjusted.
	 * @param maxLength
	 *            The max length of the string returned.
	 * @return The string with a smaller or equal length to the specified one.
	 */
	private static String quebraString(String label, int maxLength) 
	{
		try{
			return label.substring(0, label.length()-maxLength);	
		}catch(Exception e){
			// if the string is smaller than the specified length.
			return label;
		}
	}

	public static File geraArquivo(List<Entrada> entradas, List<Gasto> gastos,
									List<Conta> contas, String nomeArquivo) throws MyMoneyException {
		try{
			File saidaRelatorio = new File(nomeArquivo);
			//saidaRelatorio.createNewFile();

			RelatorioPrinter geradorRelatorio = new RelatorioPrinter(entradas, gastos, contas);
			BufferedWriter writer = new BufferedWriter(new FileWriter(saidaRelatorio));
			writer.write(geradorRelatorio.getRelatorioSimples());
			writer.close();
			
			return saidaRelatorio;
			
						
		}catch(IOException exception){
			throw new MyMoneyException("Erro ao criar o relatorio.");
		}
	}
}
